﻿using System;
using System.IO;
using System.Collections.Generic;

using Filter = System.Predicate<string>;

namespace System.Web.ScriptBuilder
{
	static class PathUtils
	{
		public static Filter DefaultFilter = (Filter)(s => false);
		public static char[] DirectorySeparatorChars = new[] { Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar };

		public static PathKind GetPathKind(string path)
		{
			if (File.Exists(path))
				return PathKind.FilePath;
			else if (path.IndexOf("*") != -1)
				return PathKind.DirectoryWithMask;
			else if (Directory.Exists(path))
				return PathKind.DirectoryPath;
			else
				return PathKind.Unknown;
		}

		public static Filter CreateFilter(string[] paths)
		{
			if (paths == null)
				return DefaultFilter;

			var exclusions = new string[paths.Length];
			var exclusionMasks = new string[paths.Length];

			for (int i = 0; i < paths.Length; i++)
			{
				var path = paths[i];
				if (path.IndexOf("*") != -1)
				{
					var mask = (string)null;
					SplitPathFromMask(ref path, ref mask);

					exclusions[i] = path;
					exclusionMasks[i] = mask;
				}
				else
				{
					exclusions[i] = path;
					exclusionMasks[i] = null;
				}
			}

			return (Predicate<string>)(filePath =>
			{
				for (int i = 0; i < exclusions.Length; i++)
				{
					var path = exclusions[i];
					var mask = exclusionMasks[i];
					if (filePath.StartsWith(path, StringComparison.OrdinalIgnoreCase))
					{
						if (string.IsNullOrEmpty(mask))
							return true;
						else
						{
							var fileName = Path.GetFileName(filePath);
							if (mask == "*" || mask == "*.*")
								return true;
							if (mask.StartsWith("*"))
								return fileName.EndsWith(mask.Substring(1));
							else if (mask.EndsWith("*"))
								return fileName.EndsWith(mask.Substring(0, mask.Length - 1));
							else
								throw new Exception(Loc.GetString("Exception_WrongFilenameMask", mask));
						}
					}
				}
				return false;
			});
		}
		public static IEnumerable<string> EnumerateFilesAt(string path, IComparer<string> comparer)
		{
			if (File.Exists(path))
			{
				yield return path;
				yield break;
			}
			else if (path.IndexOf("*") != -1)
			{
				var mask = (string)"*.*";
				SplitPathFromMask(ref path, ref mask);

				if (!Directory.Exists(path))
					yield break;

				var files = Directory.GetFiles(path, mask); Array.Sort(files, comparer);
				var directories = Directory.GetDirectories(path); Array.Sort(directories, comparer);

				foreach (var file in files)
				{
					yield return file;
				}
				foreach (var subDir in directories)
				{
					foreach (var subDirFile in PathUtils.EnumerateFilesAt(Path.Combine(subDir, mask), comparer))
						yield return subDirFile;
				}
			}
			else if (Directory.Exists(path))
			{
				var files = Directory.GetFiles(path); Array.Sort(files, comparer);
				var directories = Directory.GetDirectories(path); Array.Sort(directories, comparer);

				foreach (var file in files)
				{
					yield return file;
				}
				foreach (var subDir in directories)
				{
					foreach (var subDirFile in PathUtils.EnumerateFilesAt(subDir, comparer))
						yield return subDirFile;
				}
			}
			yield break;
		}

		public static void SplitPathFromMask(ref string path, ref string mask)
		{
			if (path.IndexOf("*") != -1)
			{
				var dirPathIndex = path.LastIndexOfAny(DirectorySeparatorChars);

				mask = path.Substring(dirPathIndex + 1);
				path = path.Substring(0, dirPathIndex + 1);
			}
		}
	}
}
